﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using RpcGenerator.CSharp;

namespace RpcGenerator
{
    class ShareFieldGenerator
    {
        public void Generate(MessageFieldDescription description, IMessageSerializer serializer,MessageFormatType format)
        {
            switch (format)
            {
                case MessageFormatType.Protobuf:
                    {
                        ShareFieldProtobufGenerator pf = new ShareFieldProtobufGenerator();
                        pf.Generate(description, serializer);
                        break;
                    }
                default:
                    {
                        BeginExtension(description, serializer);

                        BeginRead(description, serializer);
                        EndRead(serializer);

                        BeginWrite(description, serializer);
                        EndWrite(serializer);

                        EndExtension(serializer);
                        break;
                    }
            }
        }
        private void BeginExtension(MessageFieldDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine(string.Format("static class {0}Extension", description.Type.Name));
            serializer.BeginBlock();
        }

        private void EndExtension(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }
        
        #region write
        private void BeginWrite(MessageFieldDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine();
            serializer.NewLine(string.Format("public static void Write(this {0} {1},NetStream ns)", description.Type.Name, "_this"));
            serializer.BeginBlock();

            WriteObjectMask(description, serializer,"_this");

            foreach (MessageFieldMemberDescription mfmd in description.Members)
            {
                WriteField("_this", mfmd, serializer);
            }
        }

        private void WriteObjectMask(MessageFieldDescription description, IMessageSerializer serializer,string objName)
        {
            if (description.ObjectCount > 0)
            {
                int objMaskCount = description.ObjectCount / description.MemberMaskSize;
                if (objMaskCount == 0)
                    objMaskCount = 1;

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (description.MemberMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("byte objectmask{0} = 0;", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("UInt16 objectmask{0} = 0;", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("UInt32 objectmask{0} = 0;", i));
                                break;
                            }
                        case 64:
                            {
                                serializer.NewLine(string.Format("UInt64 objectmask{0} = 0;", i));
                                break;
                            }
                    }
                }

                foreach (MessageFieldMemberDescription mfmd in description.Members)
                {
                    if (!mfmd.IsPrimitiveType())
                    {
                        serializer.NewLine(string.Format("if(null=={0}.{1})", objName, mfmd.Name));
                        serializer.BeginBlock();
                            int maskIndex = (mfmd.ObjectIndex - 1) / description.MemberMaskSize;
                            int maskInnerIndex = (mfmd.ObjectIndex - 1) % description.MemberMaskSize;
                            serializer.NewLine(string.Format("objectmask{0} |= {1};", maskIndex, 1 << maskInnerIndex));
                        serializer.EndBlock();
                    }
                }

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (description.MemberMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("ns.WriteByte(objectmask{0});", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt16(objectmask{0});", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt32(objectmask{0});", i));
                                break;
                            }
                        case 64:
                            {
                                serializer.NewLine(string.Format("ns.WriteUInt64(objectmask{0});", i));
                                break;
                            }
                    }
                }
            }
        }

        private void EndWrite(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }

        private void WriteField(string objName, MessageFieldMemberDescription mfmd, IMessageSerializer serializer)
        {
            switch (mfmd.GetMessageFieldType())
            {
                case MessageFieldType.PrimitiveType:
                    {
                        WritePrimitiveType(mfmd.Type, string.Format("{0}.{1}", objName, mfmd.Name), serializer);
                        break;
                    }
                case MessageFieldType.GenericType:
                    {
                        serializer.NewLine(string.Format("if(null!={0}.{1})", objName,mfmd.Name));
                        serializer.BeginBlock();
                        serializer.NewLine(string.Format("UInt16 count = (UInt16){0}.{1}.Count;", objName, mfmd.Name));
                            WritePrimitiveType(typeof(UInt16), "count", serializer);
                            serializer.NewLine("for(UInt16 i = 0;i<count;++i)");
                            serializer.BeginBlock();
                                Type genericType = MessageFieldDescription.GetGenericType(mfmd.Type);
                                switch (MessageFieldDescription.GetMessageFieldType(genericType))
                                {
                                    case MessageFieldType.PrimitiveType:
                                        {
                                            WritePrimitiveType(genericType, string.Format("{0}.{1}[i]", objName, mfmd.Name), serializer);
                                            break;
                                        }
                                    case MessageFieldType.ClientType:
                                        {
                                            serializer.NewLine(string.Format("{0}.{1}[i].Write(ns);", objName, mfmd.Name));
                                            break;
                                        }
                                    case MessageFieldType.GenericType:
                                        {
                                            serializer.NewLine("模板中嵌套模板不支持");
                                            break;
                                        }
                                }
                            serializer.EndBlock();
                        serializer.EndBlock();
                        break;
                    }
                case MessageFieldType.ClientType:
                    {
                        serializer.NewLine(string.Format("if(null!={0}.{1})",objName,mfmd.Name));
                        serializer.BeginBlock();
                            serializer.NewLine(string.Format("{0}.{1}.Write(ns);", objName, mfmd.Name));
                        serializer.EndBlock();                        
                        break;
                    }
            }
        }

        private void WritePrimitiveType(Type tp, string name, IMessageSerializer serializer)
        {
            if (tp == typeof(sbyte))
            {
                serializer.NewLine(string.Format("ns.WriteInt8({0});", name));
                return;
            }

            if (tp == typeof(byte))
            {
                serializer.NewLine(string.Format("ns.WriteUInt8({0});", name));
                return;
            }

            if (tp == typeof(Int16))
            {
                serializer.NewLine(string.Format("ns.WriteInt16({0});", name));
                return;
            }

            if (tp == typeof(UInt16))
            {
                serializer.NewLine(string.Format("ns.WriteUInt16({0});", name));
                return;
            }

            if (tp == typeof(Int32))
            {
                serializer.NewLine(string.Format("ns.WriteInt32({0});", name));
                return;
            }

            if (tp == typeof(UInt32))
            {
                serializer.NewLine(string.Format("ns.WriteUInt32({0});", name));
                return;
            }

            if (tp == typeof(Int64))
            {
                serializer.NewLine(string.Format("ns.WriteInt64({0});", name));
                return;
            }

            if (tp == typeof(UInt64))
            {
                serializer.NewLine(string.Format("ns.WriteUInt64({0});", name));
                return;
            }

            if (tp == typeof(float))
            {
                serializer.NewLine(string.Format("ns.WriteFloat({0});", name));
                return;
            }

            if (tp == typeof(double))
            {
                serializer.NewLine(string.Format("ns.WriteFloat((float){0});", name));
                return;
            }

            if (tp == typeof(bool))
            {
                serializer.NewLine(string.Format("ns.WriteBool({0});", name));
                return;
            }

            if (tp.IsEnum)
            {
                serializer.NewLine(string.Format("ns.WriteInt32((Int32){0});",name));
                return;
            }

            if (tp == typeof(DateTime))
            {
                serializer.NewLine(string.Format("ns.WriteString({0}.ToString());", name));
                return;
            }

            if (tp == typeof(String))
            {
                serializer.NewLine(string.Format("ns.WriteString({0});", name));
            }
        }
        #endregion

        #region read
        private void BeginRead(MessageFieldDescription description, IMessageSerializer serializer)
        {
            serializer.NewLine(string.Format("public static void Read(this {0} {1},NetStream ns)", description.Type.Name, "_this"));
            serializer.BeginBlock();

            ReadObjectMask(description, serializer);

            foreach (MessageFieldMemberDescription mfmd in description.Members)
            {
                ReadField("_this", mfmd, serializer, description);
            }
        }

        private static void ReadObjectMask(MessageFieldDescription description, IMessageSerializer serializer)
        {
            if (description.ObjectCount > 0)
            {
                int objMaskCount = description.ObjectCount / description.MemberMaskSize;
                if (objMaskCount == 0)
                    objMaskCount = 1;

                for (int i = 0; i < objMaskCount; ++i)
                {
                    switch (description.MemberMaskSize)
                    {
                        case 8:
                            {
                                serializer.NewLine(string.Format("byte objectmask{0} = ns.ReadByte();", i));
                                break;
                            }
                        case 16:
                            {
                                serializer.NewLine(string.Format("UInt16 objectmask{0} = ns.ReadUInt16();", i));
                                break;
                            }
                        case 32:
                            {
                                serializer.NewLine(string.Format("UInt32 objectmask{0} = ns.ReadUInt32();", i));
                                break;
                            }
                        case 64:
                            {
                                serializer.NewLine(string.Format("UInt64 objectmask{0} = ns.ReadUInt64();", i));
                                break;
                            }
                    }
                }
            }
        }

        private void EndRead(IMessageSerializer serializer)
        {
            serializer.EndBlock();
        }

        private void ReadField(string objName, MessageFieldMemberDescription mfmd, IMessageSerializer serializer,MessageFieldDescription mfd)
        {
            switch (mfmd.GetMessageFieldType())
            {
                case MessageFieldType.PrimitiveType:
                    {
                        ReadPrimitiveType(mfmd.Type, string.Format("{0}.{1}", objName, mfmd.Name), serializer);
                        break;
                    }
                case MessageFieldType.GenericType:
                    {
                        int maskIndex = (mfmd.ObjectIndex - 1) / mfd.MemberMaskSize;
                        int maskInnerIndex = (mfmd.ObjectIndex - 1) % mfd.MemberMaskSize;
                        serializer.NewLine(string.Format("if((objectmask{0}&{1})==0)", maskIndex, 1 << maskInnerIndex));
                        serializer.BeginBlock();
                        serializer.NewLine(string.Format("{0}.{1} = new {2}();", objName, mfmd.Name, mfmd.GenericTypeName));
                        serializer.NewLine("UInt16 count = ns.ReadUInt16();");
                        serializer.NewLine("for(UInt16 i = 0;i<count;++i)");
                        serializer.BeginBlock();
                        {
                            Type genericType = MessageFieldDescription.GetGenericType(mfmd.Type);
                            switch (MessageFieldDescription.GetMessageFieldType(genericType))
                            {
                                case MessageFieldType.PrimitiveType:
                                    {
                                        serializer.NewLine(string.Format("{0} member;", genericType.Name));
                                        ReadPrimitiveType(genericType, "member", serializer);
                                        serializer.NewLine(string.Format("{0}.{1}.Add(member);", objName, mfmd.Name));
                                        break;
                                    }
                                case MessageFieldType.ClientType:
                                    {
                                        serializer.NewLine(string.Format("{0} member = new {0}();", genericType.Name));
                                        serializer.NewLine("member.Read(ns);");
                                        serializer.NewLine(string.Format("{0}.{1}.Add(member);", objName, mfmd.Name));
                                        break;
                                    }
                                case MessageFieldType.GenericType:
                                    {
                                        serializer.NewLine("模板嵌套模板不支持");
                                        break;
                                    }
                            }
                        }
                        serializer.EndBlock();
                        serializer.EndBlock();
                        break;
                    }
                case MessageFieldType.ClientType:
                    {
                        int maskIndex = (mfmd.ObjectIndex - 1) / mfd.MemberMaskSize;
                        int maskInnerIndex = (mfmd.ObjectIndex - 1) % mfd.MemberMaskSize;
                        serializer.NewLine(string.Format("if((objectmask{0}&{1})==0)", maskIndex, 1 << maskInnerIndex));
                        serializer.BeginBlock();
                            serializer.NewLine(string.Format("{0}.{1} = new {2}();", objName, mfmd.Name, mfmd.Type.Name));
                            serializer.NewLine(string.Format("{0}.{1}.Read(ns);", objName, mfmd.Name));
                        serializer.EndBlock();
                        break;
                    }
            }
        }

        private void ReadPrimitiveType(Type tp, string name, IMessageSerializer serializer)
        {
            if (tp == typeof(sbyte))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt8();", name));
                return;
            }

            if (tp == typeof(byte))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt8();", name));
                return;
            }

            if (tp == typeof(Int16))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt16();", name));
                return;
            }

            if (tp == typeof(UInt16))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt16();", name));
                return;
            }

            if (tp == typeof(Int32))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadInt32();", name));
                return;
            }

            if (tp == typeof(UInt32))
            {
                serializer.NewLine(string.Format("{0} = ns.WriteUInt32();", name));
                return;
            }

            if (tp == typeof(Int64))
            {
                serializer.NewLine(string.Format("{0} = ns.WriteInt64();", name));
                return;
            }

            if (tp == typeof(UInt64))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadUInt64();", name));
                return;
            }

            if (tp == typeof(float))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadFloat();", name));
                return;
            }

            if (tp == typeof(double))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadFloat();", name));
                return;
            }

            if (tp == typeof(bool))
            {
                serializer.NewLine(string.Format("{0} = ns.ReadBool();", name));
                return;
            }

            if (tp.IsEnum)
            {
                serializer.NewLine(string.Format("{0} = ({1})ns.ReadInt32();", name, tp.Name));
                return;
            }

            if (tp == typeof(DateTime))
            {
                serializer.BeginBlock();
                    serializer.NewLine("DateTime tmpDate;");
                    serializer.NewLine("if(DateTime.TryParse(ns.ReadString(),out tmpDate))");
                    serializer.BeginBlock();
                        serializer.NewLine(string.Format("{0} = tmpDate;", name));
                    serializer.EndBlock();   
                serializer.EndBlock();          
                return;
            }
        }
        #endregion
    }
}
